import json
import os
from PyQt5.QtWidgets import (
    QWidget,
    QGridLayout,
    QGroupBox,
    QTabWidget,
    QHBoxLayout,
    QVBoxLayout,
    QLabel,
    QStyledItemDelegate,
    QDesktopWidget,
    QPushButton,
    QFileDialog,
    QComboBox,
    QFrame,
    QScrollArea,
    QAbstractItemView,
    QTableWidgetItem,
    QTableWidget,
    QSpacerItem,
    QSizePolicy,
    QLineEdit,
    QGraphicsOpacityEffect,
    QStyle,
)
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QEvent
from PyQt5.QtGui import QPainter, QPen, QColor, QDoubleValidator, QPixmap, QIcon, QBrush
from functools import partial
from pydantic import BaseModel, Field, field_validator
import gui.controls.envs as envs

# import gui.controls.envs as envs
from ..dlg_mulit_instance import DlgMutliInstance
from ..custom_table import CustomTable
from ..dlg_params import DlgParams
from ..util.file_operation import FileOperation
from ..util.excel_formula import ExcelFormula
from ..util.type_conversion import TypeConversion
from ..custom_widget import LineEditFloat, LineEditStr, LineEditInt, SelectBox
from ..util import data_clear as DC
from enum import Enum


class DlgLifeAnalysisDFR(DlgMutliInstance):
    lifeAnalysisDFR_save_signal = pyqtSignal(object)

    def __init__(self, parent=None):
        super(DlgLifeAnalysisDFR, self).__init__(parent)

        self.setWindowTitle("DFR 疲劳分析")
        self.resize(870, 700)
        layout = QVBoxLayout()

        self.material = "铝合金"
        self.loading = False
        self.primaryCycleEnabled = False

        self.readyOnlyData = {
            "Rc": None,
            "correctedDFRbase": None,
            "DFR": None,
            "GAGRadio": None,
            "totalDamage": None,
            "ZGAG": None,
            "NGAG": None,
            "NR": None,
        }

        # 创建垂直布局
        layout = QVBoxLayout(self)
        self.widgetDict = {}

        # 创建标签页控件
        self.tabs = QTabWidget()

        # 创建示例页面
        self.materialPropertiesPage = QWidget()
        self.fatigueStressPage = QWidget()
        self.DFRPage = QWidget()
        self.lifeAnalysisResultsPage = QWidget()

        # 将页面添加到标签页控件
        self.tabs.addTab(self.materialPropertiesPage, "材料属性")
        self.tabs.addTab(self.fatigueStressPage, "疲劳应力")
        self.tabs.addTab(self.DFRPage, "DFR计算")
        self.tabs.addTab(self.lifeAnalysisResultsPage, "疲劳寿命分析结果")

        self.createMaterialPropertiesPage()
        self.createFatigueStressPage()
        self.createDFRPage()
        self.createLifeAnalysisResultsPage()

        # 将标签页控件添加到布局
        layout.addWidget(self.tabs)

        btnLayout = QHBoxLayout()
        btnLayout.setAlignment(Qt.AlignCenter)  # 设置居中对齐
        pBtnCancel = QPushButton("取消")
        pBtnCancel.setObjectName("pBtnCancel")
        pBtnCancel.clicked.connect(self.close)
        self.pBtnOK = QPushButton("保存")
        self.pBtnOK.setObjectName("pBtnOK")
        self.pBtnOK.clicked.connect(self.on_pBtnOK_clicked)
        btnLayout.addWidget(self.pBtnOK)
        btnLayout.addWidget(pBtnCancel)

        self.pBtnOK.setFocusPolicy(Qt.NoFocus)
        pBtnCancel.setFocusPolicy(Qt.NoFocus)

        layout.addLayout(btnLayout)
        self.setLayout(layout)

        self.setDefaultValues()

        self.cacheFile = "data/ipimas/uis/lifeAnalysisDFR.json"
        data = FileOperation.read_json(self.cacheFile)
        self.loadData(data)

    # def showEvent(self, event):
    #     super().showEvent(event)
    #     self.pBtnOK.setFocus()  # 在窗口显示后设置焦点

    def setDefaultValues(self):
        self.widgetDict["FRF"].setValue(1.0)
        if self.material == "铝合金":
            self.widgetDict["S"].setValue(2.0)
            self.widgetDict["MPa"].setValue(310.0)
            self.widgetDict["a"].setValue(4.0)
        elif self.material == "钛合金":
            self.widgetDict["S"].setValue(2.0)
            self.widgetDict["MPa"].setValue(620.0)
            self.widgetDict["a"].setValue(3.0)
        elif self.material == "中强钢":
            self.widgetDict["S"].setValue(1.8)
            self.widgetDict["MPa"].setValue(930.0)
            self.widgetDict["a"].setValue(3.0)
        elif self.material == "高强钢":
            self.widgetDict["S"].setValue(1.8)
            self.widgetDict["MPa"].setValue(1240.0)
            self.widgetDict["a"].setValue(2.2)

    def createMaterialPropertiesPage(self):
        layout = QHBoxLayout()

        params1 = [
            {"name": "FRF", "type": "float", "title": "可靠性系数FRF", "isInput": True},
            {"name": "S", "type": "float", "title": "S", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
        ]

        params2 = [
            {"name": "MPa", "type": "float", "title": "σm0(MPa) ", "isInput": True},
            {"name": "a", "type": "float", "title": "α", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
        ]

        gridLayout1 = QGridLayout()
        self.createParameterUI(params1, gridLayout1)

        gridLayout2 = QGridLayout()
        self.createParameterUI(params2, gridLayout2)

        layout.addLayout(gridLayout1)
        layout.addSpacerItem(
            QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
        )
        layout.addLayout(gridLayout2)
        self.materialPropertiesPage.setLayout(layout)

    def createFatigueStressPage(self):
        layout = QVBoxLayout()

        majorCycleGroupBox = QGroupBox("地-空-地循环/大循环")
        majorCycleLayout = QVBoxLayout()
        self.widgetDict["majorCycleTable"] = CustomTable(
            rows=0, columns=6, useCustomMenu=False
        )
        self.widgetDict["majorCycleTable"].flag = "majorCycle"
        self.widgetDict["majorCycleTable"].setHorizontalHeaderLabels(
            [
                "σminGAG（最小应力）",
                "σmaxGAG（最大应力）",
                "地-空-地应力比RGAG",
                "每次飞行的循环数",
                "每10^5循环的损伤",
                "每10^5飞行的损伤",
            ]
        )
        self.widgetDict["majorCycleTable"].rowSetFloat([0, 1, 2, 4, 5])
        self.widgetDict["majorCycleTable"].rowSetInt([3])
        self.widgetDict["majorCycleTable"].set_column_read_only([2, 4, 5])
        self.setTableColumnColor(self.widgetDict["majorCycleTable"], [2, 4, 5])
        self.widgetDict["majorCycleTable"].add()
        self.widgetDict["majorCycleTable"].cellChanged.connect(
            self.majorCycleCellChange
        )
        self.setTableFixeHeight(self.widgetDict["majorCycleTable"])
        majorCycleLayout.addWidget(self.widgetDict["majorCycleTable"])
        majorCycleGroupBox.setLayout(majorCycleLayout)

        primaryCycleGroupBox = QGroupBox("主循环")
        primaryCycleLayout = QVBoxLayout()

        primaryCycleBtnLayout = QHBoxLayout()
        self.primaryCycleStatus = QLabel("未启用")
        primaryCycleBtnLayout.addWidget(self.primaryCycleStatus)
        primaryCycleBtnLayout.addStretch()
        addPrimaryCycleBtn = QPushButton(self)
        self.isEnableBtn = QPushButton("启用")
        self.isEnableBtn.clicked.connect(self.on_isEnableBtn_clicked)
        primaryCycleBtnLayout.addWidget(self.isEnableBtn)
        self.set_button_image(addPrimaryCycleBtn, "icons/add.png")

        addPrimaryCycleBtn.clicked.connect(self.addPrimaryCycle)
        primaryCycleBtnLayout.addWidget(addPrimaryCycleBtn)
        delPrimaryCycleBtn = QPushButton(self)
        self.set_button_image(delPrimaryCycleBtn, "icons/del.png")
        delPrimaryCycleBtn.clicked.connect(self.delPrimaryCycle)
        primaryCycleBtnLayout.addWidget(delPrimaryCycleBtn)

        cfgIndex = {}
        with open("config/sys/index.json", "r", encoding="utf-8") as file:
            cfgIndex = json.load(file) or {}

        theme = cfgIndex.get("theme", "dark")  # dark or light
        if theme == "light":
            addPrimaryCycleBtn.setStyleSheet(
                """
                QPushButton {
                    background-color: #409eff;
                    border: none;
                }
            """
            )
            delPrimaryCycleBtn.setStyleSheet(
                """
                QPushButton {
                    background-color: #409eff;
                    border: none;
                }
            """
            )

        self.widgetDict["primaryCycleTable"] = CustomTable(
            rows=0, columns=6, useCustomMenu=False
        )
        self.widgetDict["primaryCycleTable"].flag = "primaryCycle"
        self.widgetDict["primaryCycleTable"].setHorizontalHeaderLabels(
            [
                "σminGAG（最小应力）",
                "σmaxGAG（最大应力）",
                "每个主循环应力比R",
                "每次飞行的循环数",
                "每10^5循环的损伤",
                "每10^5飞行的损伤",
            ]
        )
        self.widgetDict["primaryCycleTable"].rowSetFloat([0, 1, 2, 4, 5])
        self.widgetDict["primaryCycleTable"].rowSetInt([3])
        self.widgetDict["primaryCycleTable"].set_column_read_only([2, 4, 5])
        self.setTableColumnColor(self.widgetDict["primaryCycleTable"], [2, 4, 5])
        self.widgetDict["primaryCycleTable"].cellChanged.connect(
            self.primaryCycleCellChange
        )
        primaryCycleLayout.addLayout(primaryCycleBtnLayout)
        primaryCycleLayout.addWidget(self.widgetDict["primaryCycleTable"])
        primaryCycleGroupBox.setLayout(primaryCycleLayout)

        minorCycleGroupBox = QGroupBox("飞行情况说明/小循环")
        minorCycleLayout = QVBoxLayout()

        minorCycleBtnLayout = QHBoxLayout()
        minorCycleBtnLayout.addStretch()
        addMinorCycleBtn = QPushButton(self)
        self.set_button_image(addMinorCycleBtn, "icons/add.png")
        addMinorCycleBtn.clicked.connect(self.addMinorCycle)
        minorCycleBtnLayout.addWidget(addMinorCycleBtn)
        delMinorCycleBtn = QPushButton(self)
        self.set_button_image(delMinorCycleBtn, "icons/del.png")
        delMinorCycleBtn.clicked.connect(self.delMinorCycle)
        minorCycleBtnLayout.addWidget(delMinorCycleBtn)

        self.widgetDict["minorCycleTable"] = CustomTable(
            rows=0, columns=6, useCustomMenu=False
        )
        self.widgetDict["minorCycleTable"].flag = "minorCycle"
        self.widgetDict["minorCycleTable"].setHorizontalHeaderLabels(
            [
                "飞行工况名称",
                "平均应力",
                "应力幅",
                "每次飞行的循环数",
                "每10^5循环的损伤",
                "每10^5飞行的损伤",
            ]
        )
        self.widgetDict["minorCycleTable"].rowSetFloat([1, 2, 4, 5])
        self.widgetDict["minorCycleTable"].rowSetInt([3])
        self.widgetDict["minorCycleTable"].set_column_read_only([4, 5])
        self.setTableColumnColor(self.widgetDict["minorCycleTable"], [4, 5])
        self.widgetDict["minorCycleTable"].cellChanged.connect(
            self.minorCycleCellChange
        )
        minorCycleLayout.addLayout(minorCycleBtnLayout)
        minorCycleLayout.addWidget(self.widgetDict["minorCycleTable"])
        minorCycleGroupBox.setLayout(minorCycleLayout)

        gridLayout = QGridLayout()
        gridLayout.setColumnStretch(0, 1)
        gridLayout.setColumnStretch(1, 3)
        gridLayout.setColumnStretch(2, 1)
        gridLayout.setColumnStretch(3, 3)
        label1 = QLabel("地-空-地损伤比λ")
        # label1.setStyleSheet("color: rgba(255, 255, 255, 128);")
        gridLayout.addWidget(label1, 0, 0)
        self.widgetDict["GAGRadio"] = LineEditFloat()
        gridLayout.addWidget(self.widgetDict["GAGRadio"], 0, 1)
        self.widgetDict["GAGRadio"].setEnabled(False)
        self.widgetDict["GAGRadio"].editingFinished.connect(
            partial(self.on_text_changed, flag="GAGRadio")
        )
        label2 = QLabel("总损伤")
        # label2.setStyleSheet("color: rgba(255, 255, 255, 128);")
        gridLayout.addWidget(label2, 0, 2)
        self.widgetDict["totalDamage"] = LineEditFloat()
        gridLayout.addWidget(self.widgetDict["totalDamage"], 0, 3)
        self.widgetDict["totalDamage"].setEnabled(False)
        self.widgetDict["totalDamage"].editingFinished.connect(
            partial(self.on_text_changed, flag="totalDamage")
        )

        layout.addWidget(majorCycleGroupBox)
        layout.addWidget(primaryCycleGroupBox)
        layout.addWidget(minorCycleGroupBox)
        layout.addLayout(gridLayout)

        self.fatigueStressPage.setLayout(layout)

    def createDFRPage(self):
        params1 = [
            {"name": "A", "type": "float", "title": "孔充填系数A", "isInput": True},
            {
                "name": "B",
                "type": "float",
                "title": "合金和表面处理系数B",
                "isInput": True,
            },
            {"name": "C", "type": "float", "title": "埋头深度系数C", "isInput": True},
            {
                "name": "D",
                "type": "float",
                "title": "材料叠层厚度系数D",
                "isInput": True,
            },
            {"name": "E", "type": "float", "title": "螺栓夹紧系数E", "isInput": True},
            {"name": "F", "type": "float", "title": "粗糙度系数F", "isInput": True},
            {"name": "U", "type": "float", "title": "凸台有效系数U", "isInput": True},
        ]
        params2 = [
            {"name": "DFRbase", "type": "float", "title": "DFRbase", "isInput": True},
            {"name": "nd", "type": "int", "title": "相似关键细节数nd", "isInput": True},
            {
                "name": "initialStress",
                "type": "float",
                "title": "初始应力集中系数",
                "isInput": True,
            },
            {
                "name": "actualStress",
                "type": "float",
                "title": "实际应力集中系数",
                "isInput": True,
            },
            {
                "name": "Rc",
                "type": "float",
                "title": "疲劳额定系数Rc",
                "isInput": False,
                "enabled": False,
            },
            {
                "name": "correctedDFRbase",
                "type": "float",
                "title": "修正DFRbase",
                "isInput": False,
                "enabled": False,
            },
            {
                "name": "DFR",
                "type": "float",
                "title": "DFR",
                "isInput": False,
                "enabled": False,
            },
        ]

        layout = QHBoxLayout()

        gridLayout1 = QGridLayout()
        self.createParameterUI(params1, gridLayout1)

        gridLayout2 = QGridLayout()
        self.createParameterUI(params2, gridLayout2)

        layout.addLayout(gridLayout1)
        layout.addSpacerItem(
            QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
        )
        layout.addLayout(gridLayout2)
        self.DFRPage.setLayout(layout)

    def createLifeAnalysisResultsPage(self):

        params1 = [
            {
                "name": "ZGAG",
                "type": "float",
                "title": "ZGAG",
                "isInput": False,
                "enabled": False,
            },
            {
                "name": "NGAG",
                "type": "float",
                "title": "NGAG",
                "isInput": False,
                "enabled": False,
            },
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
        ]
        params2 = [
            {
                "name": "NR",
                "type": "float",
                "title": "可靠性寿命NR",
                "isInput": False,
                "enabled": False,
            },
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
            {"name": "", "type": "float", "title": "", "isInput": True},
        ]

        layout = QHBoxLayout()

        gridLayout1 = QGridLayout()
        self.createParameterUI(params1, gridLayout1)

        gridLayout2 = QGridLayout()
        self.createParameterUI(params2, gridLayout2)

        layout.addLayout(gridLayout1)
        layout.addSpacerItem(
            QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
        )
        layout.addLayout(gridLayout2)
        self.lifeAnalysisResultsPage.setLayout(layout)

    def on_isEnableBtn_clicked(self):
        if self.primaryCycleEnabled:
            self.primaryCycleEnabled = False
            self.isEnableBtn.setText("启用")
            self.primaryCycleStatus.setText("未启用")
        else:
            self.primaryCycleEnabled = True
            self.isEnableBtn.setText("取消启用")
            self.primaryCycleStatus.setText("已启用")

    def addPrimaryCycle(self):
        self.widgetDict["primaryCycleTable"].add()

    def delPrimaryCycle(self):
        self.widgetDict["primaryCycleTable"].delete()

    def addMinorCycle(self):
        self.widgetDict["minorCycleTable"].add()

    def delMinorCycle(self):
        self.widgetDict["minorCycleTable"].delete()

    # 地-空-地循环/大循环
    def majorCycleCellChange(self, row, col):
        if self.loading:
            return
        if col == 0 or col == 1 or col == 3:
            # self.updateValues()
            self.updateValuesALL()

    # 主循环
    def primaryCycleCellChange(self, row, col):
        if self.widgetDict["primaryCycleTable"].isAdding or self.loading:
            return
        if col == 0 or col == 1 or col == 3:
            self.updateValuesALL()

    # 飞行情况说明/小循环
    def minorCycleCellChange(self, row, col):
        if self.widgetDict["minorCycleTable"].isAdding or self.loading:
            return
        if col == 1 or col == 2 or col == 3:
            self.updateValuesALL()

    def on_text_changed(self, flag):
        if self.loading:
            return
        DFRDependList = [
            "FRF",
            "a",
            "A",
            "B",
            "C",
            "D",
            "E",
            "F",
            "U",
            "nd",
            "DFRbase",
            "actualStress",
            "initialStress",
        ]
        if flag in DFRDependList:
            # self.updateValues()
            self.updateValuesALL()
        if flag == "S" or flag == "MPa":
            self.updateValuesALL()

    def updateValues(self):
        # 获取除了主循环和小循环表格之外的所有输入
        # 材料属性
        FRF = self.widgetDict["FRF"].getValue()
        S = self.widgetDict["S"].getValue()
        MPa = self.widgetDict["MPa"].getValue()
        a = self.widgetDict["a"].getValue()
        # 大循环
        minGAG = self.widgetDict["majorCycleTable"].item(0, 0).text()
        maxGAG = self.widgetDict["majorCycleTable"].item(0, 1).text()
        cyclicNum = self.widgetDict["majorCycleTable"].item(0, 3).text()
        # DFR计算
        A = self.widgetDict["A"].getValue()
        B = self.widgetDict["B"].getValue()
        C = self.widgetDict["C"].getValue()
        D = self.widgetDict["D"].getValue()
        E = self.widgetDict["E"].getValue()
        F = self.widgetDict["F"].getValue()
        U = self.widgetDict["U"].getValue()
        N_d = self.widgetDict["nd"].getValue()
        DFRbase = self.widgetDict["DFRbase"].getValue()
        actualStress = self.widgetDict["actualStress"].getValue()
        initialStress = self.widgetDict["initialStress"].getValue()

        # 大循环 输出
        RGAGItem = self.widgetDict["majorCycleTable"].item(0, 2)
        cyclicDamageItem = self.widgetDict["majorCycleTable"].item(0, 4)
        flightDamageItem = self.widgetDict["majorCycleTable"].item(0, 5)
        RGAG = ExcelFormula.getR_GAG(minGAG, maxGAG)
        RGAGItem.setText(TypeConversion.roundToString(RGAG, 2), RGAG)
        cyclicDamage = ExcelFormula.getCyclicDamage(S, RGAG, maxGAG, MPa)
        cyclicDamageItem.setText(
            TypeConversion.roundToString(cyclicDamage, 3), cyclicDamage
        )
        flightDamage = ExcelFormula.getFlightDamage(cyclicNum, cyclicDamage)
        flightDamageItem.setText(
            TypeConversion.roundToString(flightDamage, 3), flightDamage
        )

        # 主循环和小循环的飞行损伤
        primaryCycleDamageList = self.widgetDict["primaryCycleTable"].getColumnData(
            col=5
        )
        minorCycleDamageList = self.widgetDict["minorCycleTable"].getColumnData(col=5)

        # 总损伤 地-空-地损伤比λ
        totalDamage = ExcelFormula.getTotalDamage(
            flightDamage, primaryCycleDamageList, minorCycleDamageList
        )
        GAGRadio = ExcelFormula.getGAGRadio(flightDamage, totalDamage)
        self.readyOnlyData["GAGRadio"] = GAGRadio
        self.readyOnlyData["totalDamage"] = totalDamage
        self.widgetDict["totalDamage"].setValue(
            TypeConversion.roundToString(totalDamage, 3)
        )
        self.widgetDict["GAGRadio"].setValue(TypeConversion.roundToString(GAGRadio, 3))

        # DFR计算 输出
        R_c = ExcelFormula.getR_c(N_d, S, a)
        correctedDFRbase = ExcelFormula.getCorrectedDFRbase(
            DFRbase, actualStress, initialStress
        )
        DFR = ExcelFormula.getDFR(correctedDFRbase, A, B, C, D, E, F, U, R_c)
        self.widgetDict["Rc"].setValue(TypeConversion.roundToString(R_c, 2))
        self.widgetDict["correctedDFRbase"].setValue(
            TypeConversion.roundToString(correctedDFRbase, 8)
        )
        self.widgetDict["DFR"].setValue(TypeConversion.roundToString(DFR, 3))
        self.readyOnlyData["Rc"] = R_c
        self.readyOnlyData["correctedDFRbase"] = correctedDFRbase
        self.readyOnlyData["DFR"] = DFR

        # 疲劳寿命分析结果
        Z_GAG = ExcelFormula.getZ_GAG(RGAG, MPa, DFR, maxGAG)
        self.widgetDict["ZGAG"].setValue(TypeConversion.roundToString(Z_GAG, 4))
        N_GAG = ExcelFormula.getN_GAG(Z_GAG, S)
        self.widgetDict["NGAG"].setValue(TypeConversion.roundToString(N_GAG, 0))
        N_r = ExcelFormula.getN_r(N_GAG, GAGRadio, FRF)
        self.widgetDict["NR"].setValue(str(N_r))
        self.readyOnlyData["ZGAG"] = Z_GAG
        self.readyOnlyData["NGAG"] = N_GAG
        self.readyOnlyData["NR"] = N_r

    # S和MPA修改时全部重新计算
    def updateValuesALL(self):
        # 获取除了主循环和小循环表格之外的所有输入
        # 材料属性
        FRF = self.widgetDict["FRF"].getValue()
        S = self.widgetDict["S"].getValue()
        MPa = self.widgetDict["MPa"].getValue()
        a = self.widgetDict["a"].getValue()
        # 大循环
        minGAG = self.widgetDict["majorCycleTable"].item(0, 0).text()
        maxGAG = self.widgetDict["majorCycleTable"].item(0, 1).text()
        cyclicNum = self.widgetDict["majorCycleTable"].item(0, 3).text()
        # DFR计算
        A = self.widgetDict["A"].getValue()
        B = self.widgetDict["B"].getValue()
        C = self.widgetDict["C"].getValue()
        D = self.widgetDict["D"].getValue()
        E = self.widgetDict["E"].getValue()
        F = self.widgetDict["F"].getValue()
        U = self.widgetDict["U"].getValue()
        N_d = self.widgetDict["nd"].getValue()
        DFRbase = self.widgetDict["DFRbase"].getValue()
        actualStress = self.widgetDict["actualStress"].getValue()
        initialStress = self.widgetDict["initialStress"].getValue()

        # 大循环 输出
        RGAGItem = self.widgetDict["majorCycleTable"].item(0, 2)
        cyclicDamageItem = self.widgetDict["majorCycleTable"].item(0, 4)
        flightDamageItem = self.widgetDict["majorCycleTable"].item(0, 5)
        RGAG = ExcelFormula.getR_GAG(minGAG, maxGAG)
        RGAGItem.setText(TypeConversion.roundToString(RGAG, 2), RGAG)

        cyclicDamage = ExcelFormula.getCyclicDamage(S, RGAG, maxGAG, MPa)
        cyclicDamageItem.setText(
            TypeConversion.roundToString(cyclicDamage, 3), cyclicDamage
        )
        flightDamage = ExcelFormula.getFlightDamage(cyclicNum, cyclicDamage)
        flightDamageItem.setText(
            TypeConversion.roundToString(flightDamage, 3), flightDamage
        )

        # 主循环和小循环的飞行损伤
        self.updatePrimaryCycleTable()
        self.updateMinorCycleTable()
        primaryCycleDamageList = self.widgetDict["primaryCycleTable"].getColumnData(
            col=5
        )
        minorCycleDamageList = self.widgetDict["minorCycleTable"].getColumnData(col=5)

        # 总损伤 地-空-地损伤比λ
        totalDamage = ExcelFormula.getTotalDamage(
            flightDamage, primaryCycleDamageList, minorCycleDamageList
        )
        GAGRadio = ExcelFormula.getGAGRadio(flightDamage, totalDamage)
        self.readyOnlyData["GAGRadio"] = GAGRadio
        self.readyOnlyData["totalDamage"] = totalDamage
        self.widgetDict["totalDamage"].setValue(
            TypeConversion.roundToString(totalDamage, 3)
        )
        self.widgetDict["GAGRadio"].setValue(TypeConversion.roundToString(GAGRadio, 3))

        # DFE计算 输出
        R_c = ExcelFormula.getR_c(N_d, S, a)
        correctedDFRbase = ExcelFormula.getCorrectedDFRbase(
            DFRbase, actualStress, initialStress
        )
        DFR = ExcelFormula.getDFR(correctedDFRbase, A, B, C, D, E, F, U, R_c)
        self.widgetDict["Rc"].setValue(TypeConversion.roundToString(R_c, 2))
        self.widgetDict["correctedDFRbase"].setValue(
            TypeConversion.roundToString(correctedDFRbase, 8)
        )
        self.widgetDict["DFR"].setValue(TypeConversion.roundToString(DFR, 3))
        self.readyOnlyData["Rc"] = R_c
        self.readyOnlyData["correctedDFRbase"] = correctedDFRbase
        self.readyOnlyData["DFR"] = DFR

        # 疲劳寿命分析结果
        Z_GAG = ExcelFormula.getZ_GAG(RGAG, MPa, DFR, maxGAG)
        self.widgetDict["ZGAG"].setValue(TypeConversion.roundToString(Z_GAG, 4))
        N_GAG = ExcelFormula.getN_GAG(Z_GAG, S)
        self.widgetDict["NGAG"].setValue(TypeConversion.roundToString(N_GAG, 0))
        N_r = ExcelFormula.getN_r(N_GAG, GAGRadio, FRF)
        self.widgetDict["NR"].setValue(str(N_r))
        self.readyOnlyData["ZGAG"] = Z_GAG
        self.readyOnlyData["NGAG"] = N_GAG
        self.readyOnlyData["NR"] = N_r

    def updatePrimaryCycleTable(self):
        for row in range(self.widgetDict["primaryCycleTable"].rowCount()):
            minGAGItem = self.widgetDict["primaryCycleTable"].item(row, 0)
            maxGAGItem = self.widgetDict["primaryCycleTable"].item(row, 1)
            RGAGItem = self.widgetDict["primaryCycleTable"].item(row, 2)
            cyclicNumItem = self.widgetDict["primaryCycleTable"].item(row, 3)
            cyclicDamageItem = self.widgetDict["primaryCycleTable"].item(row, 4)
            flightDamageItem = self.widgetDict["primaryCycleTable"].item(row, 5)
            RGAG = ExcelFormula.getR_GAG(minGAGItem.text(), maxGAGItem.text())
            RGAGItem.setText(TypeConversion.roundToString(RGAG, 2), RGAG)
            S = self.widgetDict["S"].getValue()
            maxGAG = maxGAGItem.text()
            MPa = self.widgetDict["MPa"].getValue()
            cyclicDamage = ExcelFormula.getCyclicDamage(S, RGAG, maxGAG, MPa)
            cyclicDamageItem.setText(
                TypeConversion.roundToString(cyclicDamage, 3), cyclicDamage
            )
            cyclicNum = cyclicNumItem.text()
            flightDamage = ExcelFormula.getFlightDamage(cyclicNum, cyclicDamage)
            flightDamageItem.setText(
                TypeConversion.roundToString(flightDamage, 3), flightDamage
            )

    def updateMinorCycleTable(self):
        for row in range(self.widgetDict["minorCycleTable"].rowCount()):
            meanStressItem = self.widgetDict["minorCycleTable"].item(row, 1)
            stressAmplitudeItem = self.widgetDict["minorCycleTable"].item(row, 2)
            cyclicNumItem = self.widgetDict["minorCycleTable"].item(row, 3)
            cyclicDamageItem = self.widgetDict["minorCycleTable"].item(row, 4)
            flightDamageItem = self.widgetDict["minorCycleTable"].item(row, 5)
            S = self.widgetDict["S"].getValue()
            MPa = self.widgetDict["MPa"].getValue()
            meanStress = meanStressItem.text()
            stressAmplitude = stressAmplitudeItem.text()
            cyclicDamage = ExcelFormula.getCyclicDamage_minorCycle(
                meanStress, stressAmplitude, S, MPa
            )
            cyclicDamageItem.setText(
                TypeConversion.roundToString(cyclicDamage, 3), cyclicDamage
            )
            cyclicNum = cyclicNumItem.text()
            flightDamage = ExcelFormula.getFlightDamage(cyclicNum, cyclicDamage)
            flightDamageItem.setText(
                TypeConversion.roundToString(flightDamage, 3), flightDamage
            )

    def createParameterUI(self, list, layout):
        for index, item in enumerate(list):
            label = QLabel(item["title"])
            layout.setColumnStretch(0, 1)
            layout.setColumnStretch(1, 2)
            layout.addWidget(label, index, 0)
            if item["title"] == "":
                widget = QLineEdit()
                op = QGraphicsOpacityEffect()
                op.setOpacity(0)
                widget.setGraphicsEffect(op)
                widget.setReadOnly(True)  # 设置为只读，不可编辑
                widget.setAttribute(
                    Qt.WA_TransparentForMouseEvents, True
                )  # 设置为不可聚焦
                layout.addWidget(widget, index, 1)
                continue
            if item["type"] == "str":
                self.widgetDict[item["name"]] = LineEditStr()
                layout.addWidget(self.widgetDict[item["name"]], index, 1)
            if item["type"] == "int":
                self.widgetDict[item["name"]] = LineEditInt()
                layout.addWidget(self.widgetDict[item["name"]], index, 1)
            elif item["type"] == "float":
                self.widgetDict[item["name"]] = LineEditFloat()
                layout.addWidget(self.widgetDict[item["name"]], index, 1)
            elif item["type"] == "select":
                self.widgetDict[item["name"]] = SelectBox(
                    options=item["options"], onChanged=item.get("onChanged")
                )
                layout.addWidget(self.widgetDict[item["name"]], index, 1)
            if item["isInput"]:
                self.widgetDict[item["name"]].editingFinished.connect(
                    partial(self.on_text_changed, flag=item["name"])
                )
            if "enabled" in item and item["enabled"] == False:
                self.widgetDict[item["name"]].setEnabled(False)
                # label.setStyleSheet("color: rgba(255, 255, 255, 128);")

    def on_pBtnOK_clicked(self):
        self.pBtnOK.setFocus()
        # 保存前重新计算 确保保存的值是经过计算后的
        self.updateValuesALL()
        data = {}
        for key, widget in self.widgetDict.items():
            if isinstance(widget, CustomTable):
                data[key] = widget.getTableData()
            else:
                if key in self.readyOnlyData:
                    data[key] = self.readyOnlyData[key]
                else:
                    data[key] = widget.getValue()
        if self.primaryCycleEnabled:
            data["primaryCycleEnabled"] = True
        else:
            data["primaryCycleEnabled"] = False
        FileOperation.write_json(self.cacheFile, data)
        descriptData = {}
        pMap = parameterMapping()
        for k, v in data.items():
            # if v or v == 0:
            if k == "majorCycleTable" and v:
                descriptData["大循环"] = listToDict(
                    v,
                    [
                        "σminGAG",
                        "σmaxGAG",
                        "地-空-地应力比RGAG",
                        "每次飞行的循环数",
                        "每10^5循环的损伤",
                        "每10^5飞行的损伤",
                    ],
                )
            elif k == "primaryCycleTable" and v:
                descriptData["主循环"] = listToDict(
                    v,
                    [
                        "σminGAG",
                        "σmaxGAG",
                        "地-空-地应力比R",
                        "每次飞行的循环数",
                        "每10^5循环的损伤",
                        "每10^5飞行的损伤",
                    ],
                )
            elif k == "minorCycleTable" and v:
                descriptData["小循环"] = listToDict(
                    v,
                    [
                        "飞行工况名称",
                        "平均应力",
                        "应力幅",
                        "每次飞行的循环数",
                        "每10^5循环的损伤",
                        "每10^5飞行的损伤",
                    ],
                )
            else:
                descriptData[pMap[k]] = v
        descriptData = DC.filter_empty_values(descriptData)
        # FileOperation.write_json("data/ipimas/uis/lifeAnalysisDFR2.json", descriptData)
        self.close()
        self.lifeAnalysisDFR_save_signal.emit(descriptData)

    def loadData(self, data):
        self.loading = True
        if data:
            for widget, value in data.items():
                if widget in self.widgetDict:
                    if isinstance(self.widgetDict[widget], CustomTable):
                        if widget == "majorCycleTable":
                            for rowData in value:
                                self.widgetDict[widget].fillRowData(
                                    row=0, rowData=rowData
                                )
                        else:
                            for rowData in value:
                                self.widgetDict[widget].addRow(rowData=rowData)
                    else:
                        if widget == "Rc":
                            self.widgetDict[widget].setValue(
                                TypeConversion.roundToString(value, 2)
                            )
                        elif widget == "correctedDFRbase":
                            self.widgetDict[widget].setValue(
                                TypeConversion.roundToString(value, 8)
                            )
                        elif (
                            widget == "GAGRadio"
                            or widget == "DFR"
                            or widget == "totalDamage"
                        ):
                            self.widgetDict[widget].setValue(
                                TypeConversion.roundToString(value, 3)
                            )
                        elif widget == "ZGAG":
                            self.widgetDict[widget].setValue(
                                TypeConversion.roundToString(value, 4)
                            )
                        elif widget == "NGAG":
                            self.widgetDict[widget].setValue(
                                TypeConversion.roundToString(value, 0)
                            )
                        else:
                            self.widgetDict[widget].setValue(value)

                if widget in self.readyOnlyData:
                    self.readyOnlyData[widget] = value

                if widget == "primaryCycleEnabled":
                    self.primaryCycleEnabled = value
                    if value == True:
                        self.isEnableBtn.setText("取消启用")
                        self.primaryCycleStatus.setText("已启用")
                    else:
                        self.isEnableBtn.setText("启用")
                        self.primaryCycleStatus.setText("未启用")
        self.loading = False

    def setTableColumnColor(self, table, columnList, color="rgba(255, 255, 255, 0.5)"):
        for column in columnList:
            header_item = table.horizontalHeaderItem(column)
            # if header_item is None:
            #     header_item = QTableWidgetItem("列3")
            #     self.tableWidget.setHorizontalHeaderItem(2, header_item)
            header_item.setForeground(QBrush(QColor(255, 255, 255, 128)))

    def set_button_image(self, button, image_path):
        """设置按钮的图片，并保持等比宽高，同时设置边距"""
        pixmap = QPixmap(image_path)
        if pixmap.isNull():
            print("Error: Unable to load image.")
            return

        # 等比缩放图片到指定大小（15x15像素）
        scaled_pixmap = pixmap.scaled(
            15, 15, Qt.KeepAspectRatio, Qt.SmoothTransformation
        )

        # 将 QPixmap 转换为 QIcon
        icon = QIcon(scaled_pixmap)

        # 设置按钮图标和大小
        button.setIcon(icon)  # 使用 QIcon 设置图标
        button.setIconSize(scaled_pixmap.size())  # 设置图标大小
        button.setFixedSize(20, 20)  # 强制设置按钮的宽高为25像素（15 + 2 * 5）

    def setTableFixeHeight(self, table):
        total_height = table.rowHeight(0)
        horizontal_header_height = table.horizontalHeader().height()
        table.setFixedHeight(total_height + horizontal_header_height)


def parameterMapping():
    return {
        "FRF": "可靠性系数FRF",
        "S": "S",
        "MPa": "σm0(MPa) ",
        "a": "α",
        "majorCycleTable": "大循环",
        "primaryCycleTable": "主循环",
        "minorCycleTable": "小循环",
        "GAGRadio": "地-空-地损伤比λ",
        "totalDamage": "总损伤",
        "A": "孔充填系数A",
        "B": "合金和表面处理系数B",
        "C": "埋头深度系数C",
        "D": "材料叠层厚度系数D",
        "E": "螺栓夹紧系数E",
        "F": "粗糙度系数F",
        "U": "凸台有效系数U",
        "DFRbase": "DFRbase",
        "nd": "相似关键细节数nd",
        "initialStress": "初始应力集中系数",
        "actualStress": "实际应力集中系数",
        "Rc": "疲劳额定系数Rc",
        "correctedDFRbase": "修正DFRbase",
        "DFR": "DFR",
        "ZGAG": "ZGAG",
        "NGAG": "NGAG",
        "NR": "可靠性寿命NR",
        "primaryCycleEnabled": "主循环启用",
    }


def listToDict(list, keys):
    dataList = []
    for li in list:
        dict = {}
        for i, item in enumerate(li):
            if item or item == 0:
                dict[keys[i]] = item
        dataList.append(dict)
    return dataList
